
package com.trydofor.id.mina;

import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.slf4j.LoggerFactory;

import com.trydofor.aloe.exception.Exceptions;
import com.trydofor.id.byta.Packet;
import com.trydofor.id.doer.PacketDoer;
import com.trydofor.id.logs.Eventable;
import com.trydofor.id.logs.Eventer;
import com.trydofor.id.logs.LogLevel;

/**
 * @author : Shi Rongjiu (www.trydofor.com)
 */
public class ServerIoHandler extends IoHandlerAdapter implements Eventable {
    
    /**
     * the events
     */
    public enum Event implements Eventer.Event {
        /**
         * register a doer
         */
        DOER_REGISTER,
        /**
         * unregister a doer
         */
        DOER_UNREGISTER,
        /**
         * doer not found
         */
        DOER_NOTFOUND,
        /**
         * doer can not reply(replies null)
         */
        DOER_NOTREPLY;
    }
    
    private final Eventer         eventer = new Eventer(LoggerFactory.getLogger(ServerIoHandler.class));
    private final Set<PacketDoer> doers   = new CopyOnWriteArraySet<PacketDoer>();
    
    /**
     * constructor
     */
    public ServerIoHandler() {
        // set default event log level
        eventer.register(Event.DOER_REGISTER, LogLevel.INFO);
        eventer.register(Event.DOER_UNREGISTER, LogLevel.INFO);
        eventer.register(Event.DOER_NOTFOUND, LogLevel.WARN);
        eventer.register(Event.DOER_NOTREPLY, LogLevel.WARN);
    }
    
    /**
     * {@inheritDoc}
     */
    public Eventer getEventer() {
        return eventer;
    }
    
    /**
     * register the doer.
     * 
     * @param doer the doer
     * @return true if registered.
     * @throws NullPointerException if doer is null
     * @see Set#add(Object)
     */
    public boolean register(PacketDoer doer) {
        Exceptions.checkNullPointer(doer);
        
        boolean added = doers.add(doer);
        eventer.log(Event.DOER_REGISTER, "adder new doer[{}], doer={}", added, doer);
        
        return added;
    }
    
    /**
     * unregister the doer.
     * 
     * @param doer the doer
     * @return true if unregistered. if doer is null return false.
     * @see Set#remove(Object)
     */
    public boolean unregister(PacketDoer doer) {
        if (doer == null) return false;
        boolean rmed = doers.remove(doer);
        return rmed;
    }
    
    /**
     * unregister the doer by its hash
     * 
     * @param hash the doer's hash
     * @return true if unregistered.
     * @see #unregister(PacketDoer)
     */
    public boolean unregister(int hash) {
        PacketDoer doer = null;
        for (Iterator<PacketDoer> it = doers.iterator(); it.hasNext();) {
            doer = it.next();
            if (doer.hashCode() == hash) break;
        }
        return unregister(doer);
    }
    
    @Override
    public void messageReceived(IoSession session, Object message) throws Exception {
        Packet request = (Packet) message; // throw
        
        PacketDoer doer = null;
        for (Iterator<PacketDoer> it = doers.iterator(); it.hasNext();) {
            PacketDoer d = it.next();
            if (d.accept(request)) {
                doer = d;
                break;
            }
        }
        
        if (doer == null) {
            eventer.log(Event.DOER_NOTFOUND, "doer not found, packet'head={}", request.getHead());
        }
        else {
            Packet response = doer.response(request);
            if (response == null) {
                eventer.log(Event.DOER_NOTREPLY, "doer not reply, doer={},head={}", doer, request.getHead());
            }
            else {
                session.write(response);
            }
        }
    }
    
    @Override
    public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
        // let logging filter do
    }
}
